#pragma once

#include <gtest/gtest.h>
#include "qlib/tests/db_test/check.h"
#include "qlib/tests/geo_test/random.h"

using namespace qlib;

Curve qlib_curve;
gdstk::Curve gdstk_curve;

void resert_curve() {
  qlib_curve.points = rand_vec2_array();
  qlib_curve.last_ctrl = Vec2d(0, 0);
  qlib_curve.tolerance = 0.01;

  gdstk_curve.point_array = to_gds(qlib_curve.points);
  gdstk_curve.last_ctrl = {0, 0};
  gdstk_curve.tolerance = 0.01;
}

int test_count = 2;
bool relative = false;

TEST(CurveHorizontalTest, BasicAssertion) {
  for (size_t i = 0; i < test_count; i++) {
    relative = !relative;  // true and false

    // Single input
    resert_curve();
    double coord_x = rand_double();
    qlib_curve.horizontal(coord_x, relative);
    gdstk_curve.horizontal(coord_x, relative);
    EXPECT_EQ(Check::check_vec2d_array(qlib_curve.points, gdstk_curve.point_array), true);

    // Multiple input
    resert_curve();
    Array<double> coord_x_array = rand_double_array();
    gdstk::Array<double> coord_x_gds_array = to_gds(coord_x_array);
    qlib_curve.horizontal(coord_x_array, relative);
    gdstk_curve.horizontal(coord_x_gds_array, relative);
    EXPECT_EQ(Check::check_vec2d_array(qlib_curve.points, gdstk_curve.point_array), true);
  }
}

TEST(CurveVerticalTest, BasicAssertion) {
  for (size_t i = 0; i < test_count; i++) {
    relative = !relative;

    resert_curve();
    double coord_x = rand_double();
    qlib_curve.vertical(coord_x, relative);
    gdstk_curve.vertical(coord_x, relative);
    EXPECT_EQ(Check::check_vec2d_array(qlib_curve.points, gdstk_curve.point_array), true);

    resert_curve();
    Array<double> coord_x_array = rand_double_array();
    gdstk::Array<double> coord_x_gds_array = to_gds(coord_x_array);
    qlib_curve.vertical(coord_x_array, relative);
    gdstk_curve.vertical(coord_x_gds_array, relative);

    EXPECT_EQ(Check::check_vec2d_array(qlib_curve.points, gdstk_curve.point_array), true);
  }
}

TEST(CurveSegmentTest, BasicAssertion) {
  for (size_t i = 0; i < test_count; i++) {
    relative = !relative;

    resert_curve();
    Vec2d end_point;
    end_point.x = rand_double();
    end_point.y = rand_double();
    gdstk::Vec2 end_point_t = {end_point.x, end_point.y};
    qlib_curve.segment(end_point, relative);
    gdstk_curve.segment(end_point_t, relative);
    EXPECT_EQ(Check::check_vec2d_array(qlib_curve.points, gdstk_curve.point_array), true);

    resert_curve();
    Vec2dArray end_points_array = rand_vec2_array();
    gdstk::Array<gdstk::Vec2> end_points_t_array = to_gds(end_points_array);
    qlib_curve.segment(end_points_array, relative);
    gdstk_curve.segment(end_points_t_array, relative);
    EXPECT_EQ(Check::check_vec2d_array(qlib_curve.points, gdstk_curve.point_array), true);
  }
}

TEST(CurveCubicTest, BasicAssertion) {
  for (size_t i = 0; i < test_count; i++) {
    relative = !relative;

    resert_curve();
    Vec2dArray end_points = rand_vec2_array();
    gdstk::Array<gdstk::Vec2> end_points_t = to_gds(end_points);
    gdstk_curve.cubic(end_points_t, relative);
    qlib_curve.cubic(end_points, relative);
    EXPECT_EQ(Check::check_vec2d_array(qlib_curve.points, gdstk_curve.point_array), true);
  }
}

TEST(CurveCubicSmoothTest, BasicAssertion) {
  for (size_t i = 0; i < test_count; i++) {
    relative = !relative;

    resert_curve();
    Vec2dArray end_points = rand_vec2_array();
    gdstk::Array<gdstk::Vec2> end_points_t = to_gds(end_points);
    qlib_curve.cubic_smooth(end_points, relative);
    gdstk_curve.cubic_smooth(end_points_t, relative);
    EXPECT_EQ(Check::check_vec2d_array(qlib_curve.points, gdstk_curve.point_array), true);
  }
}

TEST(CurveQuadraticTest, BasicAssertion) {
  for (size_t i = 0; i < test_count; i++) {
    relative = !relative;

    resert_curve();
    Vec2dArray end_points = rand_vec2_array();
    gdstk::Array<gdstk::Vec2> end_points_t = to_gds(end_points);
    gdstk_curve.quadratic(end_points_t, relative);
    qlib_curve.quadratic(end_points, relative);
    EXPECT_EQ(Check::check_vec2d_array(qlib_curve.points, gdstk_curve.point_array), true);
  }
}

TEST(CurveQuadraticSmoothTest, BasicAssertion) {
  for (size_t i = 0; i < test_count; i++) {
    relative = !relative;

    resert_curve();
    Vec2d end_point;
    end_point.x = rand_double();
    end_point.y = rand_double();
    gdstk::Vec2 end_point_t = {end_point.x, end_point.y};
    qlib_curve.quadratic_smooth(end_point, relative);
    gdstk_curve.quadratic_smooth(end_point_t, relative);
    EXPECT_EQ(Check::check_vec2d_array(qlib_curve.points, gdstk_curve.point_array), true);

    resert_curve();
    Vec2dArray end_points = rand_vec2_array();
    gdstk::Array<gdstk::Vec2> end_points_t = to_gds(end_points);
    qlib_curve.quadratic_smooth(end_points, relative);
    gdstk_curve.quadratic_smooth(end_points_t, relative);
    EXPECT_EQ(Check::check_vec2d_array(qlib_curve.points, gdstk_curve.point_array), true);
  }
}

TEST(CurveBezierTest, BasicAssertion) {
  for (size_t i = 0; i < test_count; i++) {
    relative = !relative;

    resert_curve();
    Vec2dArray end_points = rand_vec2_array();
    gdstk::Array<gdstk::Vec2> end_points_t = to_gds(end_points);
    gdstk_curve.bezier(end_points_t, relative);
    qlib_curve.bezier(end_points, relative);
    EXPECT_EQ(Check::check_vec2d_array(qlib_curve.points, gdstk_curve.point_array), true);
  }
}

TEST(CurveInterpolationTest, BasicAssertion) {
  for (size_t i = 0; i < test_count; i++) {
    relative = !relative;

    resert_curve();
    Vec2dArray end_points = rand_vec2_array();
    gdstk::Array<gdstk::Vec2> end_points_t = to_gds(end_points);
    double angles = rand_double();
    double angles_t = angles;
    bool angle_constraints = rand_double() < 0;
    bool angle_constraints_t = angle_constraints;
    Vec2dArray end_points_tension = rand_vec2_array(1000);
    gdstk::Array<gdstk::Vec2> end_points_tension_t = to_gds(end_points_tension);
    double initial_curl = rand_double();
    double final_curl = rand_double();
    bool cycle = rand_double() < 0;
    gdstk_curve.interpolation(end_points_t, &angles_t, &angle_constraints_t, end_points_tension_t.items, initial_curl, final_curl, cycle, relative);
    qlib_curve.interpolation(end_points, &angles, &angle_constraints, end_points_tension.data(), initial_curl, final_curl, cycle, relative);
    EXPECT_EQ(Check::check_vec2d_array(qlib_curve.points, gdstk_curve.point_array), true);
  }
}

TEST(CurveTurnTest, BasicAssertion) {
  for (size_t i = 0; i < test_count; i++) {
    relative = !relative;

    resert_curve();
    double radius = fabs(rand_double());
    double angle = rand_double();
    qlib_curve.turn(radius, angle);
    gdstk_curve.turn(radius, angle);
    EXPECT_EQ(Check::check_vec2d_array(qlib_curve.points, gdstk_curve.point_array), true);
  }
}